{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Handwriting_transcription.ipynb",
      "provenance": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/PacktPublishing/Modern-Computer-Vision-with-PyTorch/blob/master/Chapter15/Handwriting_transcription.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hQDnOljOkTLT",
        "outputId": "6365b8e6-8ebd-4d96-b746-730b5a1ea336",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 445
        }
      },
      "source": [
        "!wget https://www.dropbox.com/s/l2ul3upj7dkv4ou/synthetic-data.zip\n",
        "!unzip -qq synthetic-data.zip"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "--2020-10-02 13:47:42--  https://www.dropbox.com/s/l2ul3upj7dkv4ou/synthetic-data.zip\n",
            "Resolving www.dropbox.com (www.dropbox.com)... 162.125.1.1, 2620:100:6016:1::a27d:101\n",
            "Connecting to www.dropbox.com (www.dropbox.com)|162.125.1.1|:443... connected.\n",
            "HTTP request sent, awaiting response... 301 Moved Permanently\n",
            "Location: /s/raw/l2ul3upj7dkv4ou/synthetic-data.zip [following]\n",
            "--2020-10-02 13:47:42--  https://www.dropbox.com/s/raw/l2ul3upj7dkv4ou/synthetic-data.zip\n",
            "Reusing existing connection to www.dropbox.com:443.\n",
            "HTTP request sent, awaiting response... 302 Found\n",
            "Location: https://ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com/cd/0/inline/BAjxFW_dLaTkMmOdGoh3My4N7da_3j-VT2DkBl2EnrrW4KdMpt8jv6OPY_xFZ0gqcx-O4CWaSAnemKsH7OI6Y_DHDkXdn7fT49-ykIqG5asdSzLJdqv5NhQdEgJR8E8Kx1w/file# [following]\n",
            "--2020-10-02 13:47:42--  https://ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com/cd/0/inline/BAjxFW_dLaTkMmOdGoh3My4N7da_3j-VT2DkBl2EnrrW4KdMpt8jv6OPY_xFZ0gqcx-O4CWaSAnemKsH7OI6Y_DHDkXdn7fT49-ykIqG5asdSzLJdqv5NhQdEgJR8E8Kx1w/file\n",
            "Resolving ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com (ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com)... 162.125.1.15, 2620:100:6016:15::a27d:10f\n",
            "Connecting to ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com (ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com)|162.125.1.15|:443... connected.\n",
            "HTTP request sent, awaiting response... 302 Found\n",
            "Location: /cd/0/inline2/BAhvUkv9AycqRiPHpNkzR07RaD_6ZXBCqMgxjLIz4uwHAlpfR_9FTZlU_2jevSRfK5jLowo9i24CHVRMJOkflJxeTY0WgysV2_he60y8h9YmgSbSnqzFMXQ0ARbL1Var1-6SSBevFSyDc-fpvgqxQ_WoCgkCN5VIasAKwTAlyX5PZW2Fn3MA82_U_TDexbxc9uthzuGYDW9Yg3lMT5c46vzujY5ZJCJW-S4GAj-eUFhz0EmdTOZa40T_Sf1d-GgE35MKf-ShlwgT_r7HzIfz23omAKBeRxB7FGZ7akdb6lviIfJac1jtoCJO5IjaRx3T21H3nruWNMQlgBQa9_g-P7PmxV7YNmaOPxLUWggLIrCysg/file [following]\n",
            "--2020-10-02 13:47:43--  https://ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com/cd/0/inline2/BAhvUkv9AycqRiPHpNkzR07RaD_6ZXBCqMgxjLIz4uwHAlpfR_9FTZlU_2jevSRfK5jLowo9i24CHVRMJOkflJxeTY0WgysV2_he60y8h9YmgSbSnqzFMXQ0ARbL1Var1-6SSBevFSyDc-fpvgqxQ_WoCgkCN5VIasAKwTAlyX5PZW2Fn3MA82_U_TDexbxc9uthzuGYDW9Yg3lMT5c46vzujY5ZJCJW-S4GAj-eUFhz0EmdTOZa40T_Sf1d-GgE35MKf-ShlwgT_r7HzIfz23omAKBeRxB7FGZ7akdb6lviIfJac1jtoCJO5IjaRx3T21H3nruWNMQlgBQa9_g-P7PmxV7YNmaOPxLUWggLIrCysg/file\n",
            "Reusing existing connection to ucb0ff9836865991a703b67acc3e.dl.dropboxusercontent.com:443.\n",
            "HTTP request sent, awaiting response... 200 OK\n",
            "Length: 39876999 (38M) [application/zip]\n",
            "Saving to: ‘synthetic-data.zip’\n",
            "\n",
            "synthetic-data.zip  100%[===================>]  38.03M  49.7MB/s    in 0.8s    \n",
            "\n",
            "2020-10-02 13:47:44 (49.7 MB/s) - ‘synthetic-data.zip’ saved [39876999/39876999]\n",
            "\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VqAz_bnxkV6w",
        "outputId": "54ddeca6-7cb0-456b-e0c7-d1b73e309c52",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 666
        }
      },
      "source": [
        "!pip install torch_snippets torch_summary editdistance"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Collecting torch_snippets\n",
            "  Downloading https://files.pythonhosted.org/packages/99/59/4792a235f188a7777110ef99b6d3667883e14b1c96aeb25ade738812b73d/torch_snippets-0.224-py3-none-any.whl\n",
            "Collecting torch_summary\n",
            "  Downloading https://files.pythonhosted.org/packages/83/49/f9db57bcad7246591b93519fd8e5166c719548c45945ef7d2fc9fcba46fb/torch_summary-1.4.3-py3-none-any.whl\n",
            "Requirement already satisfied: editdistance in /usr/local/lib/python3.6/dist-packages (0.5.3)\n",
            "Requirement already satisfied: Pillow in /usr/local/lib/python3.6/dist-packages (from torch_snippets) (7.0.0)\n",
            "Requirement already satisfied: dill in /usr/local/lib/python3.6/dist-packages (from torch_snippets) (0.3.2)\n",
            "Collecting opencv-python-headless\n",
            "\u001b[?25l  Downloading https://files.pythonhosted.org/packages/e2/e2/6670da2b12544858657058a5db2f088a18c56d0144bef8d178ad4734b7a3/opencv_python_headless-4.4.0.44-cp36-cp36m-manylinux2014_x86_64.whl (36.7MB)\n",
            "\u001b[K     |████████████████████████████████| 36.7MB 83kB/s \n",
            "\u001b[?25hRequirement already satisfied: tqdm in /usr/local/lib/python3.6/dist-packages (from torch_snippets) (4.41.1)\n",
            "Requirement already satisfied: pandas in /usr/local/lib/python3.6/dist-packages (from torch_snippets) (1.1.2)\n",
            "Collecting loguru\n",
            "\u001b[?25l  Downloading https://files.pythonhosted.org/packages/6d/48/0a7d5847e3de329f1d0134baf707b689700b53bd3066a5a8cfd94b3c9fc8/loguru-0.5.3-py3-none-any.whl (57kB)\n",
            "\u001b[K     |████████████████████████████████| 61kB 10.0MB/s \n",
            "\u001b[?25hRequirement already satisfied: matplotlib in /usr/local/lib/python3.6/dist-packages (from torch_snippets) (3.2.2)\n",
            "Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from torch_snippets) (1.18.5)\n",
            "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas->torch_snippets) (2018.9)\n",
            "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.6/dist-packages (from pandas->torch_snippets) (2.8.1)\n",
            "Collecting aiocontextvars>=0.2.0; python_version < \"3.7\"\n",
            "  Downloading https://files.pythonhosted.org/packages/db/c1/7a723e8d988de0a2e623927396e54b6831b68cb80dce468c945b849a9385/aiocontextvars-0.2.2-py2.py3-none-any.whl\n",
            "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->torch_snippets) (1.2.0)\n",
            "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from matplotlib->torch_snippets) (0.10.0)\n",
            "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->torch_snippets) (2.4.7)\n",
            "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.7.3->pandas->torch_snippets) (1.15.0)\n",
            "Collecting contextvars==2.4; python_version < \"3.7\"\n",
            "  Downloading https://files.pythonhosted.org/packages/83/96/55b82d9f13763be9d672622e1b8106c85acb83edd7cc2fa5bc67cd9877e9/contextvars-2.4.tar.gz\n",
            "Collecting immutables>=0.9\n",
            "\u001b[?25l  Downloading https://files.pythonhosted.org/packages/99/e0/ea6fd4697120327d26773b5a84853f897a68e33d3f9376b00a8ff96e4f63/immutables-0.14-cp36-cp36m-manylinux1_x86_64.whl (98kB)\n",
            "\u001b[K     |████████████████████████████████| 102kB 15.8MB/s \n",
            "\u001b[?25hBuilding wheels for collected packages: contextvars\n",
            "  Building wheel for contextvars (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
            "  Created wheel for contextvars: filename=contextvars-2.4-cp36-none-any.whl size=7666 sha256=6993b683b673658d18613ee481dad0b50e74bf19a111f022adc3d07807a68013\n",
            "  Stored in directory: /root/.cache/pip/wheels/a5/7d/68/1ebae2668bda2228686e3c1cf16f2c2384cea6e9334ad5f6de\n",
            "Successfully built contextvars\n",
            "Installing collected packages: opencv-python-headless, immutables, contextvars, aiocontextvars, loguru, torch-snippets, torch-summary\n",
            "Successfully installed aiocontextvars-0.2.2 contextvars-2.4 immutables-0.14 loguru-0.5.3 opencv-python-headless-4.4.0.44 torch-snippets-0.224 torch-summary-1.4.3\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "H1IOQ7q9kX6Y"
      },
      "source": [
        "from torch_snippets import *\n",
        "from torchsummary import summary\n",
        "import editdistance"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-sT64jwokZJ1",
        "outputId": "20d04524-198b-4bca-cc91-efc3566c8739",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
        "fname2label = lambda fname: stem(fname).split('@')[0]\n",
        "images = Glob('synthetic-data')"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "2020-10-02 13:47:59.724 | INFO     | torch_snippets.loader:Glob:160 - 25132 files found at synthetic-data\n"
          ],
          "name": "stderr"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7N0yxeI8kafV"
      },
      "source": [
        "vocab = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'\n",
        "B,T,V = 64, 32, len(vocab) \n",
        "H,W = 32, 128 "
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "U5ImW_I4kb3Z"
      },
      "source": [
        "class OCRDataset(Dataset):\n",
        "    def __init__(self, items, vocab=vocab, preprocess_shape=(H,W), timesteps=T):\n",
        "        super().__init__()\n",
        "        self.items = items\n",
        "        self.charList = {ix+1:ch for ix,ch in enumerate(vocab)}\n",
        "        self.charList.update({0: '`'})\n",
        "        self.invCharList = {v:k for k,v in self.charList.items()}\n",
        "        self.ts = timesteps\n",
        "    def __len__(self):\n",
        "        return len(self.items)\n",
        "    def sample(self):\n",
        "        return self[randint(len(self))]\n",
        "    def __getitem__(self, ix):\n",
        "        item = self.items[ix]\n",
        "        image = cv2.imread(item, 0)\n",
        "        label = fname2label(item)\n",
        "        return image, label\n",
        "    def collate_fn(self, batch):\n",
        "        images, labels, label_lengths, label_vectors, input_lengths = [], [], [], [], []\n",
        "        for image, label in batch:\n",
        "            images.append(torch.Tensor(self.preprocess(image))[None,None])\n",
        "            label_lengths.append(len(label))\n",
        "            labels.append(label)\n",
        "            label_vectors.append(self.str2vec(label))\n",
        "            input_lengths.append(self.ts)\n",
        "        images = torch.cat(images).float().to(device)\n",
        "        label_lengths = torch.Tensor(label_lengths).long().to(device)\n",
        "        label_vectors = torch.Tensor(label_vectors).long().to(device)\n",
        "        input_lengths = torch.Tensor(input_lengths).long().to(device)\n",
        "        return images, label_vectors, label_lengths, input_lengths, labels\n",
        "    def str2vec(self, string, pad=True):\n",
        "        string = ''.join([s for s in string if s in self.invCharList])\n",
        "        val = list(map(lambda x: self.invCharList[x], string)) \n",
        "        if pad:\n",
        "            while len(val) < self.ts:\n",
        "                val.append(0)\n",
        "        return val\n",
        "    def preprocess(self, img, shape=(32,128)):\n",
        "        target = np.ones(shape)*255\n",
        "        try:\n",
        "            H, W = shape\n",
        "            h, w = img.shape\n",
        "            fx = H/h\n",
        "            fy = W/w\n",
        "            f = min(fx, fy)\n",
        "            _h = int(h*f)\n",
        "            _w = int(w*f)\n",
        "            _img = cv2.resize(img, (_w,_h))\n",
        "            target[:_h,:_w] = _img\n",
        "        except:\n",
        "            ...\n",
        "        return (255-target)/255\n",
        "    def decoder_chars(self, pred):\n",
        "        decoded = \"\"\n",
        "        last = \"\"\n",
        "        pred = pred.cpu().detach().numpy()\n",
        "        for i in range(len(pred)):\n",
        "            k = np.argmax(pred[i])\n",
        "            if k > 0 and self.charList[k] != last:\n",
        "                last = self.charList[k]\n",
        "                decoded = decoded + last\n",
        "            elif k > 0 and self.charList[k] == last:\n",
        "                continue\n",
        "            else:\n",
        "                last = \"\"\n",
        "        return decoded.replace(\" \",\" \")\n",
        "    def wer(self, preds, labels):\n",
        "        c = 0\n",
        "        for p, l in zip(preds, labels):\n",
        "            c += p.lower().strip() != l.lower().strip()\n",
        "        return round(c/len(preds), 4)\n",
        "    def cer(self, preds, labels):\n",
        "        c, d = [], []\n",
        "        for p, l in zip(preds, labels):\n",
        "            c.append(editdistance.eval(p, l) / len(l))\n",
        "        return round(np.mean(c), 4)\n",
        "    def evaluate(self, model, ims, labels, lower=False):\n",
        "        model.eval()\n",
        "        preds = model(ims).permute(1,0,2) # B, T, V+1\n",
        "        preds = [self.decoder_chars(pred) for pred in preds]\n",
        "        return {'char-error-rate': self.cer(preds, labels),\n",
        "                'word-error-rate': self.wer(preds, labels),\n",
        "                'char-accuracy' : 1 - self.cer(preds, labels),\n",
        "                'word-accuracy' : 1 - self.wer(preds, labels)}\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "z1zYLnvmke6-",
        "outputId": "e1e9c4a9-8aed-4857-edf2-02f26622a45f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "from sklearn.model_selection import train_test_split\n",
        "trn_items, val_items = train_test_split(Glob('synthetic-data'), test_size=0.2, random_state=22)\n",
        "trn_ds = OCRDataset(trn_items)\n",
        "val_ds = OCRDataset(val_items)\n",
        "\n",
        "trn_dl = DataLoader(trn_ds, batch_size=B, collate_fn=trn_ds.collate_fn, drop_last=True, shuffle=True)\n",
        "val_dl = DataLoader(val_ds, batch_size=B, collate_fn=val_ds.collate_fn, drop_last=True)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "2020-10-02 13:49:48.111 | INFO     | torch_snippets.loader:Glob:160 - 25132 files found at synthetic-data\n"
          ],
          "name": "stderr"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ywO4IMsZk2g1"
      },
      "source": [
        "from torch_snippets import Reshape, Permute\n",
        "class BasicBlock(nn.Module):\n",
        "    def __init__(self, ni, no, ks=3, st=1, padding=1, pool=2, drop=0.2):\n",
        "        super().__init__()\n",
        "        self.ks = ks\n",
        "        self.block = nn.Sequential(\n",
        "            nn.Conv2d(ni, no, kernel_size=ks, stride=st, padding=padding),\n",
        "            nn.BatchNorm2d(no, momentum=0.3),\n",
        "            nn.ReLU(inplace=True),\n",
        "            nn.MaxPool2d(pool),\n",
        "            nn.Dropout2d(drop)\n",
        "        )\n",
        "    def forward(self, x):\n",
        "        return self.block(x)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "q3K_qrPek42x"
      },
      "source": [
        "class Ocr(nn.Module):\n",
        "    def __init__(self, vocab):\n",
        "        super().__init__()\n",
        "        self.model = nn.Sequential(\n",
        "            BasicBlock( 1, 128),\n",
        "            BasicBlock(128, 128),\n",
        "            BasicBlock(128, 256, pool=(4,2)),\n",
        "            Reshape(-1, 256, 32),\n",
        "            Permute(2, 0, 1) # T, B, D\n",
        "        )\n",
        "        self.rnn = nn.Sequential(\n",
        "            nn.LSTM(256, 256, num_layers=2, dropout=0.2, bidirectional=True),\n",
        "        )\n",
        "        self.classification = nn.Sequential(\n",
        "            nn.Linear(512, vocab+1),\n",
        "            nn.LogSoftmax(-1),\n",
        "        )\n",
        "    def forward(self, x):\n",
        "        x = self.model(x)\n",
        "        x, lstm_states = self.rnn(x)\n",
        "        y = self.classification(x)\n",
        "        return y"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7oc3qXBRk7Fh"
      },
      "source": [
        "def ctc(log_probs, target, input_lengths, target_lengths, blank=0):\n",
        "    loss = nn.CTCLoss(blank=blank, zero_infinity=True)\n",
        "    ctc_loss = loss(log_probs, target, input_lengths, target_lengths)\n",
        "    return ctc_loss"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CIsrWALYk-wh",
        "outputId": "8a328366-c12c-48d0-9d94-b7ea0f396ee0",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 986
        }
      },
      "source": [
        "model = Ocr(len(vocab)).to(device)\n",
        "!pip install torch_summary\n",
        "from torchsummary import summary\n",
        "summary(model, torch.zeros((1,1,32,128)).to(device))"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Requirement already satisfied: torch_summary in /usr/local/lib/python3.6/dist-packages (1.4.3)\n",
            "==========================================================================================\n",
            "Layer (type:depth-idx)                   Output Shape              Param #\n",
            "==========================================================================================\n",
            "├─Sequential: 1-1                        [-1, 1, 256]              --\n",
            "|    └─BasicBlock: 2-1                   [-1, 128, 16, 64]         --\n",
            "|    |    └─Sequential: 3-1              [-1, 128, 16, 64]         1,536\n",
            "|    └─BasicBlock: 2-2                   [-1, 128, 8, 32]          --\n",
            "|    |    └─Sequential: 3-2              [-1, 128, 8, 32]          147,840\n",
            "|    └─BasicBlock: 2-3                   [-1, 256, 2, 16]          --\n",
            "|    |    └─Sequential: 3-3              [-1, 256, 2, 16]          295,680\n",
            "|    └─Reshape: 2-4                      [-1, 256, 32]             --\n",
            "|    └─Permute: 2-5                      [-1, 1, 256]              --\n",
            "├─Sequential: 1-2                        [-1, 1, 512]              --\n",
            "|    └─LSTM: 2-6                         [-1, 1, 512]              2,629,632\n",
            "├─Sequential: 1-3                        [-1, 1, 53]               --\n",
            "|    └─Linear: 2-7                       [-1, 1, 53]               27,189\n",
            "|    └─LogSoftmax: 2-8                   [-1, 1, 53]               --\n",
            "==========================================================================================\n",
            "Total params: 3,101,877\n",
            "Trainable params: 3,101,877\n",
            "Non-trainable params: 0\n",
            "Total mult-adds (M): 237.84\n",
            "==========================================================================================\n",
            "Input size (MB): 0.02\n",
            "Forward/backward pass size (MB): 11.00\n",
            "Params size (MB): 11.83\n",
            "Estimated Total Size (MB): 22.85\n",
            "==========================================================================================\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "==========================================================================================\n",
              "Layer (type:depth-idx)                   Output Shape              Param #\n",
              "==========================================================================================\n",
              "├─Sequential: 1-1                        [-1, 1, 256]              --\n",
              "|    └─BasicBlock: 2-1                   [-1, 128, 16, 64]         --\n",
              "|    |    └─Sequential: 3-1              [-1, 128, 16, 64]         1,536\n",
              "|    └─BasicBlock: 2-2                   [-1, 128, 8, 32]          --\n",
              "|    |    └─Sequential: 3-2              [-1, 128, 8, 32]          147,840\n",
              "|    └─BasicBlock: 2-3                   [-1, 256, 2, 16]          --\n",
              "|    |    └─Sequential: 3-3              [-1, 256, 2, 16]          295,680\n",
              "|    └─Reshape: 2-4                      [-1, 256, 32]             --\n",
              "|    └─Permute: 2-5                      [-1, 1, 256]              --\n",
              "├─Sequential: 1-2                        [-1, 1, 512]              --\n",
              "|    └─LSTM: 2-6                         [-1, 1, 512]              2,629,632\n",
              "├─Sequential: 1-3                        [-1, 1, 53]               --\n",
              "|    └─Linear: 2-7                       [-1, 1, 53]               27,189\n",
              "|    └─LogSoftmax: 2-8                   [-1, 1, 53]               --\n",
              "==========================================================================================\n",
              "Total params: 3,101,877\n",
              "Trainable params: 3,101,877\n",
              "Non-trainable params: 0\n",
              "Total mult-adds (M): 237.84\n",
              "==========================================================================================\n",
              "Input size (MB): 0.02\n",
              "Forward/backward pass size (MB): 11.00\n",
              "Params size (MB): 11.83\n",
              "Estimated Total Size (MB): 22.85\n",
              "=========================================================================================="
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 16
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Hs7CY0BYlAn1"
      },
      "source": [
        "def train_batch(data, model, optimizer, criterion):\n",
        "    model.train()\n",
        "    imgs, targets, label_lens, input_lens, labels = data\n",
        "    optimizer.zero_grad()\n",
        "    preds = model(imgs)\n",
        "    loss = criterion(preds, targets, input_lens, label_lens)\n",
        "    loss.backward()\n",
        "    optimizer.step()\n",
        "    results = trn_ds.evaluate(model, imgs.to(device), labels)\n",
        "    return loss, results"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dpyuNswnlmSC"
      },
      "source": [
        "@torch.no_grad()\n",
        "def validate_batch(data, model):\n",
        "    model.eval()\n",
        "    imgs, targets, label_lens, input_lens, labels = data\n",
        "    preds = model(imgs)\n",
        "    loss = criterion(preds, targets, input_lens, label_lens)\n",
        "    return loss, val_ds.evaluate(model, imgs.to(device), labels)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "yzGBIQ-qloEB"
      },
      "source": [
        "model = Ocr(len(vocab)).to(device)\n",
        "criterion = ctc\n",
        "\n",
        "optimizer = optim.AdamW(model.parameters(), lr=3e-3)\n",
        "\n",
        "n_epochs = 50\n",
        "log = Report(n_epochs)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6zmJcfsklpl8",
        "outputId": "306fd2be-6265-448b-8a59-1e35382fae17",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        }
      },
      "source": [
        "for ep in range( n_epochs):\n",
        "    # if ep in lr_schedule: optimizer = AdamW(ocr.parameters(), lr=lr_schedule[ep])\n",
        "    N = len(trn_dl)\n",
        "    for ix, data in enumerate(trn_dl):\n",
        "        pos = ep + (ix+1)/N\n",
        "        loss, results = train_batch(data, model, optimizer, criterion)\n",
        "        # scheduler.step()\n",
        "        ca, wa = results['char-accuracy'], results['word-accuracy']\n",
        "        log.record(pos=pos, trn_loss=loss, trn_char_acc=ca, trn_word_acc=wa, end='\\r')\n",
        "    val_results = []\n",
        "    N = len(val_dl)\n",
        "    for ix, data in enumerate(val_dl):\n",
        "        pos = ep + (ix+1)/N\n",
        "        loss, results = validate_batch(data, model)\n",
        "        ca, wa = results['char-accuracy'], results['word-accuracy']\n",
        "        log.record(pos=pos, val_loss=loss, val_char_acc=ca, val_word_acc=wa, end='\\r')\n",
        "\n",
        "    log.report_avgs(ep+1)\n",
        "    print()\n",
        "    for jx in range(5):\n",
        "        img, label = val_ds.sample()\n",
        "        _img = torch.Tensor(val_ds.preprocess(img)[None,None]).to(device)\n",
        "        pred = model(_img)[:,0,:]\n",
        "        pred = trn_ds.decoder_chars(pred)\n",
        "        print(f'Pred: `{pred}` :: Truth: `{label}`')\n",
        "    print()"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "EPOCH: 1.000\ttrn_loss: 3.327\ttrn_char_acc: 0.012\ttrn_word_acc: 0.000\tval_loss: 3.033\tval_char_acc: 0.053\tval_word_acc: 0.000\t(56.00s - 2744.13s remaining)\n",
            "\n",
            "Pred: `b` :: Truth: `finish`\n",
            "Pred: `s` :: Truth: `attack`\n",
            "Pred: `b` :: Truth: `beautiful`\n",
            "Pred: `m` :: Truth: `represent`\n",
            "Pred: `m` :: Truth: `what`\n",
            "\n",
            "EPOCH: 2.000\ttrn_loss: 2.633\ttrn_char_acc: 0.214\ttrn_word_acc: 0.005\tval_loss: 1.957\tval_char_acc: 0.423\tval_word_acc: 0.031\t(87.98s - 2111.44s remaining)\n",
            "\n",
            "Pred: `huaa` :: Truth: `know`\n",
            "Pred: `mrd` :: Truth: `really`\n",
            "Pred: `sentrer` :: Truth: `control`\n",
            "Pred: `wet` :: Truth: `west`\n",
            "Pred: `pareene` :: Truth: `someone`\n",
            "\n",
            "EPOCH: 3.000\ttrn_loss: 1.615\ttrn_char_acc: 0.590\ttrn_word_acc: 0.124\tval_loss: 1.164\tval_char_acc: 0.667\tval_word_acc: 0.204\t(119.94s - 1879.07s remaining)\n",
            "\n",
            "Pred: `theony` :: Truth: `theory`\n",
            "Pred: `t` :: Truth: `I`\n",
            "Pred: `whin` :: Truth: `worker`\n",
            "Pred: `liod` :: Truth: `word`\n",
            "Pred: `dinectod` :: Truth: `director`\n",
            "\n",
            "EPOCH: 4.000\ttrn_loss: 1.095\ttrn_char_acc: 0.747\ttrn_word_acc: 0.314\tval_loss: 0.801\tval_char_acc: 0.777\tval_word_acc: 0.371\t(151.87s - 1746.51s remaining)\n",
            "\n",
            "Pred: `evening` :: Truth: `evening`\n",
            "Pred: `plauer` :: Truth: `player`\n",
            "Pred: `wide` :: Truth: `wide`\n",
            "Pred: `shale` :: Truth: `shake`\n",
            "Pred: `forocess` :: Truth: `process`\n",
            "\n",
            "EPOCH: 5.000\ttrn_loss: 0.832\ttrn_char_acc: 0.826\ttrn_word_acc: 0.469\tval_loss: 0.616\tval_char_acc: 0.830\tval_word_acc: 0.492\t(183.95s - 1655.54s remaining)\n",
            "\n",
            "Pred: `raise` :: Truth: `raise`\n",
            "Pred: `commercial` :: Truth: `commercial`\n",
            "Pred: `note` :: Truth: `note`\n",
            "Pred: `bring` :: Truth: `bring`\n",
            "Pred: `assume` :: Truth: `assume`\n",
            "\n",
            "EPOCH: 6.000\ttrn_loss: 0.656\ttrn_char_acc: 0.874\ttrn_word_acc: 0.580\tval_loss: 0.499\tval_char_acc: 0.864\tval_word_acc: 0.571\t(216.05s - 1584.37s remaining)\n",
            "\n",
            "Pred: `nlion` :: Truth: `million`\n",
            "Pred: `actiuity` :: Truth: `activity`\n",
            "Pred: `imgine` :: Truth: `imagine`\n",
            "Pred: `tax` :: Truth: `tax`\n",
            "Pred: `dshernt` :: Truth: `different`\n",
            "\n",
            "EPOCH: 7.000\ttrn_loss: 0.531\ttrn_char_acc: 0.906\ttrn_word_acc: 0.671\tval_loss: 0.428\tval_char_acc: 0.880\tval_word_acc: 0.605\t(248.11s - 1524.09s remaining)\n",
            "\n",
            "Pred: `region` :: Truth: `region`\n",
            "Pred: `to` :: Truth: `to`\n",
            "Pred: `senvice` :: Truth: `service`\n",
            "Pred: `individual` :: Truth: `individual`\n",
            "Pred: `activity` :: Truth: `activity`\n",
            "\n",
            "EPOCH: 8.000\ttrn_loss: 0.439\ttrn_char_acc: 0.926\ttrn_word_acc: 0.729\tval_loss: 0.359\tval_char_acc: 0.900\tval_word_acc: 0.665\t(280.34s - 1471.77s remaining)\n",
            "\n",
            "Pred: `berubucan` :: Truth: `Republican`\n",
            "Pred: `inpotaut` :: Truth: `important`\n",
            "Pred: `suggest` :: Truth: `suggest`\n",
            "Pred: `TV` :: Truth: `TV`\n",
            "Pred: `not` :: Truth: `not`\n",
            "\n",
            "EPOCH: 9.000\ttrn_loss: 0.375\ttrn_char_acc: 0.942\ttrn_word_acc: 0.778\tval_loss: 0.318\tval_char_acc: 0.910\tval_word_acc: 0.692\t(312.57s - 1423.91s remaining)\n",
            "\n",
            "Pred: `political` :: Truth: `political`\n",
            "Pred: `thue` :: Truth: `thus`\n",
            "Pred: `allo` :: Truth: `allow`\n",
            "Pred: `experaly` :: Truth: `especially`\n",
            "Pred: `admit` :: Truth: `admit`\n",
            "\n",
            "EPOCH: 10.000\ttrn_loss: 0.325\ttrn_char_acc: 0.952\ttrn_word_acc: 0.812\tval_loss: 0.291\tval_char_acc: 0.920\tval_word_acc: 0.720\t(344.94s - 1379.75s remaining)\n",
            "\n",
            "Pred: `choam` :: Truth: `clear`\n",
            "Pred: `just` :: Truth: `just`\n",
            "Pred: `southern` :: Truth: `southern`\n",
            "Pred: `add` :: Truth: `add`\n",
            "Pred: `family` :: Truth: `family`\n",
            "\n",
            "EPOCH: 11.000\ttrn_loss: 0.282\ttrn_char_acc: 0.961\ttrn_word_acc: 0.844\tval_loss: 0.278\tval_char_acc: 0.923\tval_word_acc: 0.732\t(377.24s - 1337.47s remaining)\n",
            "\n",
            "Pred: `green` :: Truth: `green`\n",
            "Pred: `check` :: Truth: `check`\n",
            "Pred: `proride` :: Truth: `provide`\n",
            "Pred: `expert` :: Truth: `expert`\n",
            "Pred: `leter` :: Truth: `letter`\n",
            "\n",
            "EPOCH: 12.000\ttrn_loss: 0.249\ttrn_char_acc: 0.967\ttrn_word_acc: 0.863\tval_loss: 0.244\tval_char_acc: 0.931\tval_word_acc: 0.762\t(409.68s - 1297.32s remaining)\n",
            "\n",
            "Pred: `particwlarly` :: Truth: `particularly`\n",
            "Pred: `watch` :: Truth: `watch`\n",
            "Pred: `try` :: Truth: `try`\n",
            "Pred: `now` :: Truth: `now`\n",
            "Pred: `tatal` :: Truth: `total`\n",
            "\n",
            "EPOCH: 13.000\ttrn_loss: 0.223\ttrn_char_acc: 0.972\ttrn_word_acc: 0.885\tval_loss: 0.234\tval_char_acc: 0.937\tval_word_acc: 0.779\t(441.96s - 1257.89s remaining)\n",
            "\n",
            "Pred: `improve` :: Truth: `improve`\n",
            "Pred: `require` :: Truth: `require`\n",
            "Pred: `from` :: Truth: `from`\n",
            "Pred: `various` :: Truth: `various`\n",
            "Pred: `high` :: Truth: `high`\n",
            "\n",
            "EPOCH: 14.000\ttrn_loss: 0.207\ttrn_char_acc: 0.976\ttrn_word_acc: 0.901\tval_loss: 0.216\tval_char_acc: 0.938\tval_word_acc: 0.791\t(474.01s - 1218.89s remaining)\n",
            "\n",
            "Pred: `wide` :: Truth: `wide`\n",
            "Pred: `clearly` :: Truth: `clearly`\n",
            "Pred: `reduce` :: Truth: `reduce`\n",
            "Pred: `hot` :: Truth: `hot`\n",
            "Pred: `into` :: Truth: `into`\n",
            "\n",
            "EPOCH: 15.000\ttrn_loss: 0.184\ttrn_char_acc: 0.980\ttrn_word_acc: 0.911\tval_loss: 0.215\tval_char_acc: 0.941\tval_word_acc: 0.792\t(506.08s - 1180.84s remaining)\n",
            "\n",
            "Pred: `item` :: Truth: `item`\n",
            "Pred: `bank` :: Truth: `bank`\n",
            "Pred: `decide` :: Truth: `decide`\n",
            "Pred: `apainst` :: Truth: `against`\n",
            "Pred: `none` :: Truth: `none`\n",
            "\n",
            "EPOCH: 16.000\ttrn_loss: 0.165\ttrn_char_acc: 0.983\ttrn_word_acc: 0.924\tval_loss: 0.208\tval_char_acc: 0.944\tval_word_acc: 0.799\t(538.20s - 1143.68s remaining)\n",
            "\n",
            "Pred: `especially` :: Truth: `especially`\n",
            "Pred: `oder` :: Truth: `order`\n",
            "Pred: `main` :: Truth: `main`\n",
            "Pred: `husband` :: Truth: `husband`\n",
            "Pred: `power` :: Truth: `power`\n",
            "\n",
            "EPOCH: 17.000\ttrn_loss: 0.156\ttrn_char_acc: 0.985\ttrn_word_acc: 0.931\tval_loss: 0.198\tval_char_acc: 0.946\tval_word_acc: 0.812\t(570.16s - 1106.78s remaining)\n",
            "\n",
            "Pred: `us` :: Truth: `us`\n",
            "Pred: `program` :: Truth: `program`\n",
            "Pred: `case` :: Truth: `case`\n",
            "Pred: `change` :: Truth: `change`\n",
            "Pred: `customer` :: Truth: `customer`\n",
            "\n",
            "EPOCH: 18.000\ttrn_loss: 0.142\ttrn_char_acc: 0.987\ttrn_word_acc: 0.941\tval_loss: 0.192\tval_char_acc: 0.947\tval_word_acc: 0.815\t(602.21s - 1070.60s remaining)\n",
            "\n",
            "Pred: `father` :: Truth: `father`\n",
            "Pred: `bank` :: Truth: `bank`\n",
            "Pred: `property` :: Truth: `property`\n",
            "Pred: `respond` :: Truth: `respond`\n",
            "Pred: `reason` :: Truth: `reason`\n",
            "\n",
            "EPOCH: 19.000\ttrn_loss: 0.133\ttrn_char_acc: 0.988\ttrn_word_acc: 0.946\tval_loss: 0.195\tval_char_acc: 0.946\tval_word_acc: 0.816\t(634.27s - 1034.85s remaining)\n",
            "\n",
            "Pred: `middle` :: Truth: `middle`\n",
            "Pred: `heatt` :: Truth: `health`\n",
            "Pred: `finally` :: Truth: `finally`\n",
            "Pred: `artist` :: Truth: `artist`\n",
            "Pred: `bad` :: Truth: `bad`\n",
            "\n",
            "EPOCH: 20.000\ttrn_loss: 0.126\ttrn_char_acc: 0.990\ttrn_word_acc: 0.951\tval_loss: 0.182\tval_char_acc: 0.950\tval_word_acc: 0.827\t(666.38s - 999.57s remaining)\n",
            "\n",
            "Pred: `station` :: Truth: `station`\n",
            "Pred: `research` :: Truth: `research`\n",
            "Pred: `thing` :: Truth: `thing`\n",
            "Pred: `or` :: Truth: `or`\n",
            "Pred: `name` :: Truth: `name`\n",
            "\n",
            "EPOCH: 21.000\ttrn_loss: 0.120\ttrn_char_acc: 0.991\ttrn_word_acc: 0.957\tval_loss: 0.180\tval_char_acc: 0.950\tval_word_acc: 0.829\t(698.30s - 964.32s remaining)\n",
            "\n",
            "Pred: `woman` :: Truth: `woman`\n",
            "Pred: `run` :: Truth: `run`\n",
            "Pred: `many` :: Truth: `many`\n",
            "Pred: `another` :: Truth: `another`\n",
            "Pred: `reduce` :: Truth: `reduce`\n",
            "\n",
            "EPOCH: 22.000\ttrn_loss: 0.112\ttrn_char_acc: 0.992\ttrn_word_acc: 0.960\tval_loss: 0.195\tval_char_acc: 0.948\tval_word_acc: 0.816\t(730.23s - 929.39s remaining)\n",
            "\n",
            "Pred: `fear` :: Truth: `fear`\n",
            "Pred: `less` :: Truth: `less`\n",
            "Pred: `raise` :: Truth: `raise`\n",
            "Pred: `commercial` :: Truth: `commercial`\n",
            "Pred: `history` :: Truth: `history`\n",
            "\n",
            "EPOCH: 23.000\ttrn_loss: 0.106\ttrn_char_acc: 0.993\ttrn_word_acc: 0.965\tval_loss: 0.174\tval_char_acc: 0.953\tval_word_acc: 0.835\t(762.23s - 894.79s remaining)\n",
            "\n",
            "Pred: `decade` :: Truth: `decade`\n",
            "Pred: `occur` :: Truth: `occur`\n",
            "Pred: `worber` :: Truth: `worker`\n",
            "Pred: `south` :: Truth: `south`\n",
            "Pred: `bill` :: Truth: `bill`\n",
            "\n",
            "EPOCH: 24.000\ttrn_loss: 0.098\ttrn_char_acc: 0.994\ttrn_word_acc: 0.969\tval_loss: 0.193\tval_char_acc: 0.951\tval_word_acc: 0.828\t(794.16s - 860.34s remaining)\n",
            "\n",
            "Pred: `force` :: Truth: `force`\n",
            "Pred: `alone` :: Truth: `alone`\n",
            "Pred: `significant` :: Truth: `significant`\n",
            "Pred: `posible` :: Truth: `possible`\n",
            "Pred: `woman` :: Truth: `woman`\n",
            "\n",
            "EPOCH: 25.000\ttrn_loss: 0.097\ttrn_char_acc: 0.994\ttrn_word_acc: 0.971\tval_loss: 0.194\tval_char_acc: 0.951\tval_word_acc: 0.823\t(826.14s - 826.14s remaining)\n",
            "\n",
            "Pred: `deep` :: Truth: `deep`\n",
            "Pred: `table` :: Truth: `table`\n",
            "Pred: `across` :: Truth: `across`\n",
            "Pred: `production` :: Truth: `production`\n",
            "Pred: `ife` :: Truth: `life`\n",
            "\n",
            "EPOCH: 26.000\ttrn_loss: 0.087\ttrn_char_acc: 0.995\ttrn_word_acc: 0.975\tval_loss: 0.187\tval_char_acc: 0.953\tval_word_acc: 0.839\t(858.06s - 792.05s remaining)\n",
            "\n",
            "Pred: `sign` :: Truth: `sign`\n",
            "Pred: `local` :: Truth: `local`\n",
            "Pred: `nove` :: Truth: `move`\n",
            "Pred: `sense` :: Truth: `sense`\n",
            "Pred: `pesple` :: Truth: `people`\n",
            "\n",
            "EPOCH: 27.000\ttrn_loss: 0.087\ttrn_char_acc: 0.996\ttrn_word_acc: 0.976\tval_loss: 0.177\tval_char_acc: 0.955\tval_word_acc: 0.844\t(889.98s - 758.13s remaining)\n",
            "\n",
            "Pred: `than` :: Truth: `than`\n",
            "Pred: `reveal` :: Truth: `reveal`\n",
            "Pred: `culture` :: Truth: `culture`\n",
            "Pred: `neturk` :: Truth: `network`\n",
            "Pred: `could` :: Truth: `could`\n",
            "\n",
            "EPOCH: 28.000\ttrn_loss: 0.079\ttrn_char_acc: 0.996\ttrn_word_acc: 0.980\tval_loss: 0.188\tval_char_acc: 0.953\tval_word_acc: 0.836\t(921.99s - 724.42s remaining)\n",
            "\n",
            "Pred: `heavy` :: Truth: `heavy`\n",
            "Pred: `along` :: Truth: `along`\n",
            "Pred: `loss` :: Truth: `loss`\n",
            "Pred: `law` :: Truth: `law`\n",
            "Pred: `wst` :: Truth: `just`\n",
            "\n",
            "EPOCH: 29.000\ttrn_loss: 0.083\ttrn_char_acc: 0.996\ttrn_word_acc: 0.980\tval_loss: 0.175\tval_char_acc: 0.955\tval_word_acc: 0.845\t(954.07s - 690.88s remaining)\n",
            "\n",
            "Pred: `wall` :: Truth: `wall`\n",
            "Pred: `capital` :: Truth: `capital`\n",
            "Pred: `method` :: Truth: `method`\n",
            "Pred: `stratiagy` :: Truth: `strategy`\n",
            "Pred: `moden` :: Truth: `modern`\n",
            "\n",
            "EPOCH: 30.000\ttrn_loss: 0.075\ttrn_char_acc: 0.997\ttrn_word_acc: 0.982\tval_loss: 0.182\tval_char_acc: 0.955\tval_word_acc: 0.835\t(986.04s - 657.36s remaining)\n",
            "\n",
            "Pred: `national` :: Truth: `national`\n",
            "Pred: `campaign` :: Truth: `campaign`\n",
            "Pred: `school` :: Truth: `school`\n",
            "Pred: `deacly` :: Truth: `clearly`\n",
            "Pred: `imagine` :: Truth: `imagine`\n",
            "\n",
            "EPOCH: 31.000\ttrn_loss: 0.079\ttrn_char_acc: 0.997\ttrn_word_acc: 0.983\tval_loss: 0.183\tval_char_acc: 0.956\tval_word_acc: 0.840\t(1018.02s - 623.95s remaining)\n",
            "\n",
            "Pred: `pretty` :: Truth: `pretty`\n",
            "Pred: `until` :: Truth: `until`\n",
            "Pred: `plan` :: Truth: `plan`\n",
            "Pred: `attack` :: Truth: `attack`\n",
            "Pred: `plan` :: Truth: `plan`\n",
            "\n",
            "EPOCH: 32.000\ttrn_loss: 0.069\ttrn_char_acc: 0.997\ttrn_word_acc: 0.986\tval_loss: 0.173\tval_char_acc: 0.957\tval_word_acc: 0.845\t(1050.00s - 590.62s remaining)\n",
            "\n",
            "Pred: `imporlitant` :: Truth: `important`\n",
            "Pred: `establish` :: Truth: `establish`\n",
            "Pred: `certain` :: Truth: `certain`\n",
            "Pred: `lot` :: Truth: `lot`\n",
            "Pred: `it` :: Truth: `it`\n",
            "\n",
            "EPOCH: 33.000\ttrn_loss: 0.067\ttrn_char_acc: 0.997\ttrn_word_acc: 0.985\tval_loss: 0.171\tval_char_acc: 0.958\tval_word_acc: 0.853\t(1082.03s - 557.41s remaining)\n",
            "\n",
            "Pred: `strong` :: Truth: `strong`\n",
            "Pred: `apply` :: Truth: `apply`\n",
            "Pred: `hotel` :: Truth: `hotel`\n",
            "Pred: `hluge` :: Truth: `huge`\n",
            "Pred: `afect` :: Truth: `affect`\n",
            "\n",
            "EPOCH: 34.000\ttrn_loss: 0.066\ttrn_char_acc: 0.998\ttrn_word_acc: 0.988\tval_loss: 0.172\tval_char_acc: 0.956\tval_word_acc: 0.846\t(1114.13s - 524.30s remaining)\n",
            "\n",
            "Pred: `local` :: Truth: `local`\n",
            "Pred: `page` :: Truth: `page`\n",
            "Pred: `agreement` :: Truth: `agreement`\n",
            "Pred: `line` :: Truth: `line`\n",
            "Pred: `ask` :: Truth: `ask`\n",
            "\n",
            "EPOCH: 35.000\ttrn_loss: 0.064\ttrn_char_acc: 0.998\ttrn_word_acc: 0.988\tval_loss: 0.188\tval_char_acc: 0.956\tval_word_acc: 0.843\t(1146.25s - 491.25s remaining)\n",
            "\n",
            "Pred: `debate` :: Truth: `debate`\n",
            "Pred: `plone` :: Truth: `phone`\n",
            "Pred: `huge` :: Truth: `huge`\n",
            "Pred: `role` :: Truth: `role`\n",
            "Pred: `check` :: Truth: `check`\n",
            "\n",
            "EPOCH: 36.000\ttrn_loss: 0.063\ttrn_char_acc: 0.998\ttrn_word_acc: 0.988\tval_loss: 0.166\tval_char_acc: 0.958\tval_word_acc: 0.850\t(1178.41s - 458.27s remaining)\n",
            "\n",
            "Pred: `science` :: Truth: `science`\n",
            "Pred: `naolue` :: Truth: `realize`\n",
            "Pred: `mother` :: Truth: `mother`\n",
            "Pred: `onto` :: Truth: `onto`\n",
            "Pred: `standerd` :: Truth: `standard`\n",
            "\n",
            "EPOCH: 37.000\ttrn_loss: 0.060\ttrn_char_acc: 0.998\ttrn_word_acc: 0.989\tval_loss: 0.164\tval_char_acc: 0.958\tval_word_acc: 0.853\t(1210.56s - 425.33s remaining)\n",
            "\n",
            "Pred: `form` :: Truth: `form`\n",
            "Pred: `management` :: Truth: `management`\n",
            "Pred: `focus` :: Truth: `focus`\n",
            "Pred: `security` :: Truth: `security`\n",
            "Pred: `girl` :: Truth: `goal`\n",
            "\n",
            "EPOCH: 38.000\ttrn_loss: 0.056\ttrn_char_acc: 0.999\ttrn_word_acc: 0.991\tval_loss: 0.164\tval_char_acc: 0.959\tval_word_acc: 0.856\t(1242.82s - 392.47s remaining)\n",
            "\n",
            "Pred: `cuch` :: Truth: `such`\n",
            "Pred: `increase` :: Truth: `increase`\n",
            "Pred: `big` :: Truth: `big`\n",
            "Pred: `indeer` :: Truth: `indeed`\n",
            "Pred: `their` :: Truth: `their`\n",
            "\n",
            "EPOCH: 39.000\ttrn_loss: 0.057\ttrn_char_acc: 0.999\ttrn_word_acc: 0.991\tval_loss: 0.167\tval_char_acc: 0.959\tval_word_acc: 0.855\t(1275.05s - 359.63s remaining)\n",
            "\n",
            "Pred: `attention` :: Truth: `attention`\n",
            "Pred: `law` :: Truth: `law`\n",
            "Pred: `blood` :: Truth: `blood`\n",
            "Pred: `simply` :: Truth: `simply`\n",
            "Pred: `find` :: Truth: `find`\n",
            "\n",
            "EPOCH: 40.000\ttrn_loss: 0.052\ttrn_char_acc: 0.999\ttrn_word_acc: 0.992\tval_loss: 0.166\tval_char_acc: 0.959\tval_word_acc: 0.860\t(1307.23s - 326.81s remaining)\n",
            "\n",
            "Pred: `result` :: Truth: `result`\n",
            "Pred: `mission` :: Truth: `mission`\n",
            "Pred: `authority` :: Truth: `authority`\n",
            "Pred: `lawyer` :: Truth: `lawyer`\n",
            "Pred: `interest` :: Truth: `interest`\n",
            "\n",
            "EPOCH: 41.000\ttrn_loss: 0.057\ttrn_char_acc: 0.998\ttrn_word_acc: 0.990\tval_loss: 0.170\tval_char_acc: 0.959\tval_word_acc: 0.862\t(1339.29s - 293.99s remaining)\n",
            "\n",
            "Pred: `can` :: Truth: `can`\n",
            "Pred: `forward` :: Truth: `forward`\n",
            "Pred: `camera` :: Truth: `camera`\n",
            "Pred: `pretty` :: Truth: `pretty`\n",
            "Pred: `collection` :: Truth: `collection`\n",
            "\n",
            "EPOCH: 42.000\ttrn_loss: 0.052\ttrn_char_acc: 0.999\ttrn_word_acc: 0.992\tval_loss: 0.174\tval_char_acc: 0.959\tval_word_acc: 0.856\t(1371.56s - 261.25s remaining)\n",
            "\n",
            "Pred: `mission` :: Truth: `mission`\n",
            "Pred: `relationstip` :: Truth: `relationship`\n",
            "Pred: `at` :: Truth: `at`\n",
            "Pred: `adult` :: Truth: `adult`\n",
            "Pred: `property` :: Truth: `property`\n",
            "\n",
            "EPOCH: 43.000\ttrn_loss: 0.055\ttrn_char_acc: 0.999\ttrn_word_acc: 0.992\tval_loss: 0.164\tval_char_acc: 0.960\tval_word_acc: 0.856\t(1403.68s - 228.51s remaining)\n",
            "\n",
            "Pred: `article` :: Truth: `article`\n",
            "Pred: `appear` :: Truth: `appear`\n",
            "Pred: `also` :: Truth: `also`\n",
            "Pred: `beat` :: Truth: `beat`\n",
            "Pred: `expect` :: Truth: `expect`\n",
            "\n",
            "EPOCH: 44.000\ttrn_loss: 0.048\ttrn_char_acc: 0.999\ttrn_word_acc: 0.993\tval_loss: 0.164\tval_char_acc: 0.961\tval_word_acc: 0.864\t(1435.79s - 195.79s remaining)\n",
            "\n",
            "Pred: `successful` :: Truth: `successful`\n",
            "Pred: `oushide` :: Truth: `outside`\n",
            "Pred: `identify` :: Truth: `identify`\n",
            "Pred: `indicate` :: Truth: `indicate`\n",
            "Pred: `usually` :: Truth: `usually`\n",
            "\n",
            "EPOCH: 45.000\ttrn_loss: 0.046\ttrn_char_acc: 0.999\ttrn_word_acc: 0.994\tval_loss: 0.192\tval_char_acc: 0.957\tval_word_acc: 0.849\t(1467.94s - 163.10s remaining)\n",
            "\n",
            "Pred: `though` :: Truth: `though`\n",
            "Pred: `blue` :: Truth: `blue`\n",
            "Pred: `country` :: Truth: `country`\n",
            "Pred: `produce` :: Truth: `produce`\n",
            "Pred: `reseut` :: Truth: `result`\n",
            "\n",
            "EPOCH: 46.000\ttrn_loss: 0.050\ttrn_char_acc: 0.999\ttrn_word_acc: 0.993\tval_loss: 0.172\tval_char_acc: 0.959\tval_word_acc: 0.858\t(1500.16s - 130.45s remaining)\n",
            "\n",
            "Pred: `such` :: Truth: `such`\n",
            "Pred: `director` :: Truth: `director`\n",
            "Pred: `land` :: Truth: `land`\n",
            "Pred: `some` :: Truth: `some`\n",
            "Pred: `civil` :: Truth: `civil`\n",
            "\n",
            "EPOCH: 47.000\ttrn_loss: 0.045\ttrn_char_acc: 0.999\ttrn_word_acc: 0.994\tval_loss: 0.160\tval_char_acc: 0.961\tval_word_acc: 0.865\t(1532.37s - 97.81s remaining)\n",
            "\n",
            "Pred: `wife` :: Truth: `wife`\n",
            "Pred: `environmental` :: Truth: `environmental`\n",
            "Pred: `opportunity` :: Truth: `opportunity`\n",
            "Pred: `movie` :: Truth: `movie`\n",
            "Pred: `identify` :: Truth: `identify`\n",
            "\n",
            "EPOCH: 48.000\ttrn_loss: 0.045\ttrn_char_acc: 0.999\ttrn_word_acc: 0.993\tval_loss: 0.182\tval_char_acc: 0.958\tval_word_acc: 0.851\t(1564.78s - 65.20s remaining)\n",
            "\n",
            "Pred: `help` :: Truth: `help`\n",
            "Pred: `brother` :: Truth: `brother`\n",
            "Pred: `run` :: Truth: `run`\n",
            "Pred: `accept` :: Truth: `accept`\n",
            "Pred: `mouth` :: Truth: `mouth`\n",
            "\n",
            "EPOCH: 49.000\ttrn_loss: 0.049\ttrn_char_acc: 0.999\ttrn_word_acc: 0.994\tval_loss: 0.165\tval_char_acc: 0.961\tval_word_acc: 0.861\t(1597.20s - 32.60s remaining)\n",
            "\n",
            "Pred: `catch` :: Truth: `catch`\n",
            "Pred: `deep` :: Truth: `deep`\n",
            "Pred: `I` :: Truth: `I`\n",
            "Pred: `lawyer` :: Truth: `lawyer`\n",
            "Pred: `both` :: Truth: `both`\n",
            "\n",
            "EPOCH: 50.000\ttrn_loss: 0.048\ttrn_char_acc: 0.999\ttrn_word_acc: 0.994\tval_loss: 0.168\tval_char_acc: 0.960\tval_word_acc: 0.863\t(1629.53s - 0.00s remaining)\n",
            "\n",
            "Pred: `nor` :: Truth: `nor`\n",
            "Pred: `esrcialy` :: Truth: `especially`\n",
            "Pred: `born` :: Truth: `born`\n",
            "Pred: `few` :: Truth: `few`\n",
            "Pred: `western` :: Truth: `western`\n",
            "\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QDpczoK0lvcu",
        "outputId": "09196e3b-3206-4f66-a4f0-01ebd2df6671",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 494
        }
      },
      "source": [
        "log.plot_epochs(['trn_word_acc','val_word_acc'], title='Training and validation word accuracy')"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "  0%|          | 0/50 [00:00<?, ?it/s]/usr/local/lib/python3.6/dist-packages/numpy/core/fromnumeric.py:3335: RuntimeWarning: Mean of empty slice.\n",
            "  out=out, **kwargs)\n",
            "/usr/local/lib/python3.6/dist-packages/numpy/core/_methods.py:161: RuntimeWarning: invalid value encountered in double_scalars\n",
            "  ret = ret.dtype.type(ret / rcount)\n",
            "100%|██████████| 50/50 [00:00<00:00, 499.58it/s]\n"
          ],
          "name": "stderr"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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BFNNSbyioiIE0YRuv0oppZpWKEendwN2+bze7Uzbdywb/cP761i/N//wa4/Hg9vtPpZNMrBrPA9MO7HG+XfffTdbt25l6NChhIeHExUVRWJiIj/99BPPPfccs2fPJikpibVr1zJixAheeeUVv/3FK1as4H//93955513ePfdd7nkkkvIy8vD6/UycOBAtm3bxurVq7nppps4dOgQxx13HHPmzCExMZGJEycydOhQli1bxqWXXsrEiRO55pprADjrrLNqfX/p6elceeWVFBUVAfDUU09x6qmnAvDwww/zyiuv4HK5mDp1Kvfeey9btmzhpptu4uDBg7jdbt58802OO+64hhavUkqpBmoRp5iJyA3YJneSk5NJS0s7Yn5CQgIFBQUAlJeV4/F4Ds8zxhzxuiHKy8oPb9+f++67jzVr1rB06VKWLl3KjBkz+Prrr0lNTWXp0qV8//33fPPNN3Tp0oUzzzyTjz/+mDFjxhy1nb59+/L9999TUFDAkiVLOOGEE/j888+pqKhg+PDhFBQUcMUVV/DII48wduxYHnroIe69914efvhhPB4PhYWFfPbZZwCMGTOGRx99lNNOO4377rsPr9db43uIjo7mnXfeISoqii1btnDttdfy+eefs3jxYt555x0++eQTYmJiyM7OxuPxcMkll3DnnXcybdo0SkpKat12SUnJUZ+XOlJhYaGWUSPTMm1coSxPYwwlHjhUbij3gteAx4DXmMN/G5/ncDdEuCDCLYQ7zxEuO93lVJ68xm6r3APlXkOFF/vaa/B4QQQE32c54rXdn/GJxefZaxARBiXVXnlsrDINZRLfA/Twed3dmXYUY8xzwHMAI0eONNUvGr9hw4bD5y4/dOHQI+Y1xXnisbGxuFwu4uLiiImJ4ZRTTmHw4MEAh18PGDAAgBEjRnDgwIEaY+rbty+7d+9m9erV3HXXXaxcuRKPx8Ppp5+O1+slPz+fqVOnAnDDDTcwY8YM4uLicLvdXHnllcTFxZGbm0t+fj5TptghCddeey1LliypcZ9er5dbb72V1atX43a72bRpE3FxcXz11Vdcd911JCcnAxAXF8fevXvZv38/l1122eFptYmKimLYsGEBlmjbojeXaHxtsUyNMZRWeMkrLievuJx857mwtIJyj6Hc46WswmufPV7KKwxlHg/lHoPHax82MdpE5nVee4zhYEYp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\n",
            "text/plain": [
              "<Figure size 576x432 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        }
      ]
    }
  ]
}